package com.bestcem.xm.common.core.uitls;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.google.protobuf.Timestamp;
import com.google.protobuf.util.Timestamps;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * 时间工具类
 *
 * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
 * @version v1.0
 * @date 2021/6/29 11:30
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtil {

    /**
     * UTC
     */
    public static final String UTC = "UTC";

    /**
     * UTC 时区
     */
    public static final ZoneOffset UTC_ZONE = ZoneOffset.of("+0");
    /**
     * Beijing 时区
     */
    public static final ZoneOffset BEIJING_ZONE = ZoneOffset.of("+8");
    /**
     * UTC 时区
     */
    public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone(ZoneId.ofOffset(UTC, UTC_ZONE));

    /**
     * Beijing 时区
     */
    public static final TimeZone BEIJING_TIME_ZONE = TimeZone.getTimeZone(ZoneId.ofOffset(UTC, BEIJING_ZONE));

    /**
     * java 时间格式化字符串统一格式
     */
    public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * yyyy-MM-dd格式
     */
    public static final String YYYY_MM_DD = "yyyy-MM-dd";

    /**
     * protobuf 输入时间转 java 时间统一时区 | 北京时间
     */
    public static final ZoneOffset INPUT_ZONE = BEIJING_ZONE;
    /**
     * java 时间转 protobuf 输出时间统一时区 | 北京时间
     */
    public static final ZoneOffset OUTPUT_ZONE = BEIJING_ZONE;
    /**
     * java 时间存入 MongoDB 时间统一时区 | UTC
     */
    public static final ZoneOffset INSERT_ZONE = UTC_ZONE;
    /**
     * MongoDB 时间取出为 java 时间统一时区 | UTC
     */
    public static final ZoneOffset SELECT_ZONE = UTC_ZONE;

    /**
     * 联系人导出文件日期格式
     */
    public static final String MEMBER_EXPORT_FORMAT = "yyyy/MM/dd HH:mm:ss";

    /**
     * java 时间格式化生成工单编号专用
     */
    public static final String DATETIME_PATTERN_FORTICKETNO = "yyyyMMdd";

    /**
     * 飞书专用格式
     */
    public static final String DATETIME_PATTERN_FEISHU = "yyyy/MM/dd HH:mm";

    /**
     * 交互日期格式
     */
    public static final String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss";

    /**
     * 前端交互日期格式
     */
    public static final String OUT_DATETIME_PATTERN = "yyyy/MM/dd HH:mm:ss";

    /**
     * 前端交互日期格式
     */
    public static final String OUT_DATE_PATTERN = "yyyyMMdd";

    /**
     * 获取当前时间
     *
     * @return 当前时间
     */
    public static Date getDate() {
        return new Date();
    }

    public static Date now() {
        return new Date();
    }

    /**
     * 获取时间
     *
     * @param date
     * @return
     */
    public static Date getDate(Date date) {
        return date == null ? getDate() : date;
    }

    /**
     * 字符串转化时间
     */
    public static ServiceResult<Date> parse(String dateTime) {
        Date date;
        try {
            date = cn.hutool.core.date.DateUtil.parse(dateTime);
        } catch (Exception e) {
            return ServiceResult.fail("日期错误，请检查");
        }
        return ServiceResult.success(date);
    }

    /**
     * java.time.LocalDateTime 格式化为字符串
     *
     * @param localDateTime LocalDateTime
     * @return Time string in {@link DateUtil#NORM_DATETIME_PATTERN} format
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 14:36
     */
    public static String formatString(LocalDateTime localDateTime) {
        return DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN).format(localDateTime);
    }

    /**
     * java.time.LocalDateTime 格式化为字符串
     *
     * @param localDateTime LocalDateTime
     * @return Time string in {@link DateUtil#NORM_DATETIME_PATTERN} format
     * @author gy.liu
     * @date 2021/6/29 14:36
     */
    public static String formatStringForTicket(LocalDateTime localDateTime) {
        return DateTimeFormatter.ofPattern(DATETIME_PATTERN_FORTICKETNO).format(localDateTime);
    }

    /**
     * java.time.LocalDateTime 格式化为字符串
     *
     * @param localDateTime LocalDateTime
     * @return Time string in {@link DateUtil#NORM_DATETIME_PATTERN} format
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 14:36
     */
    public static String formatString(LocalDateTime localDateTime, String format) {
        return DateTimeFormatter.ofPattern(format).format(localDateTime);
    }

    /**
     * java.util.Date 转换成 protobuf.Timestamp
     *
     * @param date Date
     * @return A Timestamp from the number of milliseconds elapsed from the epoch.
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     */
    public static Timestamp getTimestamp(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), BEIJING_ZONE);
        return getTimestamp(localDateTime);
    }

    /**
     * java.time.LocalDateTime 转换成 protobuf.Timestamp
     *
     * @param localDateTime LocalDateTime
     * @return A Timestamp from the number of milliseconds elapsed from the epoch.
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     */
    public static Timestamp getTimestamp(LocalDateTime localDateTime) {
        Instant instant = localDateTime.toInstant(OUTPUT_ZONE);
        return Timestamps.fromMillis(instant.toEpochMilli());
    }

    /**
     * protobuf.Timestamp 转换成 java.time.LocalDateTime
     *
     * @param timestamp protobuf.timestamp
     * @return LocalDateTime
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     **/
    public static LocalDateTime getLocalDateTime(Timestamp timestamp) {
        return Instant.ofEpochMilli(Timestamps.toMillis(timestamp)).atZone(INPUT_ZONE).toLocalDateTime();
    }

    /**
     * 字符串 转换成 java.time.LocalDateTime
     *
     * @param timeString 格式为 yyyy-MM-dd HH:mm:ss
     * @return LocalDateTime
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     **/
    public static LocalDateTime getLocalDateTime(String timeString) {
        if (StringUtils.isBlank(timeString)) {
            return null;
        }
        if (timeString.contains(".")) {
            timeString = timeString.split("\\.")[0];
        }
        DateTime dateTime = cn.hutool.core.date.DateUtil.parse(timeString);
        if (dateTime != null) {
            return Instant.ofEpochMilli(dateTime.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
        }
        return null;
    }

    /**
     * 字符串 转换成 java.time.LocalDateTime
     *
     * @param timeString 格式为 yyyy-MM-dd HH:mm:ss
     * @return LocalDateTime
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     **/
    public static Date getDate(String timeString) {
        if (StringUtils.isBlank(timeString)) {
            return null;
        }
        if (timeString.contains(".")) {
            timeString = timeString.split("\\.")[0];
        }
        return cn.hutool.core.date.DateUtil.parse(timeString);
    }

    /**
     * localDateTime中时分秒转换为对应的秒
     *
     * @param localDateTime
     * @return long
     * @author jy.zhao
     * @date 2021/8/20 14:46
     **/
    public static long hmsToSecond(LocalDateTime localDateTime) {
        return localDateTime.getHour() * 3600 + localDateTime.getMinute() * 60 + localDateTime.getSecond();
    }

    /**
     * localDateTime转换为对应的秒
     *
     * @param localDateTime
     * @return long
     * @author jy.zhao
     * @date 2021/8/20 14:46
     **/
    public static long toSecond(LocalDateTime localDateTime) {
        return localDateTime.toEpochSecond(BEIJING_ZONE);
    }

    /**
     * protobuf.Timestamp 转换成 java.util.Date
     *
     * @param timestamp protobuf.timestamp
     * @return LocalDateTime
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     **/
    public static Date getDate(Timestamp timestamp) {
        LocalDateTime localDateTime = getLocalDateTime(timestamp);
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 当前时间转换成 +8 小时的 java.util.Date 时间
     * 用于 MongoDB 插入时间
     *
     * @return LocalDateTime
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:30
     * @deprecated 替换方法见 {@link DateUtil#getCommonDate()}
     **/
    @Deprecated
    public static Date getMongoDate() {
        return getCommonDate();
    }

    /**
     * java.util.Date 时间转换成 +8 小时的 java.util.Date 时间
     * 用于 MongoDB 插入时间
     *
     * @return Date
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:47
     * @deprecated 替换方法见 {@link DateUtil#getCommonDate(Date)}
     **/
    @Deprecated
    public static Date getMongoDate(Date date) {
        return getCommonDate(date);
    }

    /**
     * java.time.LocalDateTime 时间转换成 +8 小时的 java.util.Date 时间
     * 用于 MongoDB 插入时间
     *
     * @return Date
     * @author XiaoYunSong <ys.xiao@idiaoyan.cn>
     * @date 2021/6/29 11:47
     * @deprecated 替换方法见 {@link DateUtil#getCommonDate(LocalDateTime)}
     **/
    @Deprecated
    public static Date getMongoDate(LocalDateTime localDateTime) {
        return getCommonDate(localDateTime);
    }

    /**
     * 获取当前时间
     *
     * @return LocalDateTime
     * @author jy.zhao
     * @date 2021/8/5 16:47
     **/
    public static LocalDateTime getCurrentTime() {
        return LocalDateTime.now(INPUT_ZONE);
    }

    /**
     * localDateTime 转 Date
     *
     * @param localDateTime
     * @return Date
     * @author jy.zhao
     * @date 2021/9/3 0:03
     **/
    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * java.time.LocalDateTime 时间转换成 +8 小时的 java.util.Date 时间
     * 用于 MongoDB 插入时间
     *
     * @return Date
     * @author xuan.yang
     * @date 2021/9/10 10:05
     **/
    public static Date getTemplateDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.toInstant(BEIJING_ZONE));
    }

    /**
     * 获取utc时间
     *
     * @deprecated 对应方法见 {@link DateUtil#getMongoDate()}
     */
    @Deprecated
    public static Date utcNow() {
        Instant instant = LocalDateTime.now().toInstant(ZoneOffset.UTC);
        return Date.from(instant);
    }

    /**
     * 获取本地时间（数据库保存的是UTC+8时间）
     * 查询结果的时间，是库时间+8(即UTC+16)的时间，由于保存的时间是跟本地一致的，所以需要减8小时
     *
     * @param date
     * @return java.time.LocalDateTime
     * @author zhangfeng <feng.zhang@idiaoyan.com>
     * @date 2021/7/27 15:08
     */
    public static LocalDateTime toLocalTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), BEIJING_ZONE);
    }

    /**
     * date 根据指定格式转换 string
     *
     * @param date
     * @param formatter
     * @return
     */
    public static String convertDteToString(Date date, String formatter) {
        return cn.hutool.core.date.DateUtil.format(date, formatter);
    }

    /**
     * string 根据指定格式转换 Date
     *
     * @param date
     * @param formatter
     * @return
     */
    public static Date convertStringToDate(String date, String formatter) {
        return cn.hutool.core.date.DateUtil.parse(date, formatter).toJdkDate();
    }


    /**
     * 获取当前的数据库时间
     *
     * @return Date
     * @author jy.zhao
     * @date 2021/11/9 17:47
     **/
    public static Date getCommonDate() {
        return Date.from(LocalDateTime.now().toInstant(BEIJING_ZONE));
    }

    /**
     * 获取当前的数据库时间
     *
     * @return Date
     * @author jy.zhao
     * @date 2021/11/9 17:47
     **/
    public static Date getCommonDate(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneOffset.systemDefault());
        return getCommonDate(localDateTime);
    }

    /**
     * 获取当前的数据库时间
     *
     * @return Date
     * @author jy.zhao
     * @date 2021/11/9 17:47
     **/
    public static Date getCommonDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.toInstant(BEIJING_ZONE));
    }

    /**
     * 计算年龄
     *
     * @param birthdayTime 生日
     * @param currentTime  截止时间
     */
    public static int calculateAge(Date birthdayTime, Date currentTime) {
        Calendar birthdayCalendar = Calendar.getInstance();
        birthdayCalendar.setTime(birthdayTime);
        Integer birthdayYear = birthdayCalendar.get(Calendar.YEAR);
        Integer birthdayMonth = birthdayCalendar.get(Calendar.MONTH);
        Integer birthdayDay = birthdayCalendar.get(Calendar.DAY_OF_MONTH);

        Calendar currentTimeCalendar = Calendar.getInstance();
        currentTimeCalendar.setTime(currentTime);
        Integer currentTimeYear = currentTimeCalendar.get(Calendar.YEAR);
        Integer currentTimeMonth = currentTimeCalendar.get(Calendar.MONTH);
        Integer currentTimeDay = currentTimeCalendar.get(Calendar.DAY_OF_MONTH);

        Integer age = currentTimeYear - birthdayYear;
        if (birthdayMonth > currentTimeMonth || (birthdayMonth.equals(currentTimeMonth) && birthdayDay > currentTimeDay)) {
            age--;
        }
        return age;
    }

    /**
     * 计算年龄
     *
     * @param dateStr
     * @return
     */
    public static Integer ageOfNow(String dateStr) {
        if (StrUtil.isBlank(dateStr)) {
            return 0;
        }
        Date date = convertStringToDate(dateStr, "yyyy/M/d");
        return calculateAge(date, new Date());
    }

    /**
     * 获取当天剩余时间
     *
     * @return
     */
    public static Long getRemainingTimeToday() {
        LocalDateTime midnight = LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
        long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), midnight);
        return seconds;
    }

    /**
     * 前端时间格式化
     *
     * @param date 时间
     * @return 格式化后的时间字符串
     */
    public static String dateFormatToWeb(Date date) {
        return new SimpleDateFormat(DATE_FORMAT).format(date);
    }

    /**
     * 获取日期的年月日，将时分秒置0
     *
     * @param date
     * @return
     */
    public static Date getYearMonthDay(Date date) {
        if (date == null) return null;
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 将时分秒,毫秒域清零
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    /**
     * 获取昨天日期
     */
    public static Date getYesterdayDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        return calendar.getTime();
    }
}
